package perspectives;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;


public class PropertyManager extends JPanel{
	
	ArrayList<java.lang.Class> acceptedTypes;
	ArrayList<PropertyWidgetFactory> widgetFactories;
	
	ArrayList<Property> props;
	ArrayList<PropertyWidget> widgets;
	
	protected String name;
	
	PropertyManagerGroup propertyManagerGroup = null;
	
	Font font;
	
	Vector<String> groupNames = new Vector<String>();
	Vector<JPanel> groupPanels = new Vector<JPanel>();
	
	Vector<Boolean> groupStates = new Vector<Boolean>();
	
	TaskObserverDialog taskObserverDialog;
	
	public String getName()
	{
		return name;
	}	
	public void setFont(Font f)
	{
		this.font = f;
		setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createTitledBorder(null,"Properties",TitledBorder.LEFT, TitledBorder.TOP,font),
                BorderFactory.createEmptyBorder(5,5,5,5)));
		
		setRecursiveFont(this,f);
	}
	private void setRecursiveFont(JComponent c, Font f)
	{
		if (c != this)
			c.setFont(f);
		for (int i=0; i<c.getComponentCount(); i++)
			setRecursiveFont((JComponent)c.getComponent(i),f);
	}
	
	
	abstract class PropertyWidget extends JPanel{
		
		private PropertyManager pm;
		public Property p;
		
		private boolean changeFromOutside = false;
		
		public PropertyWidget()
		{			
		}
		
		public void setReferences(Property p, PropertyManager pm)
		{
			this.pm = pm;
			this.p = p;
		}
		
		public abstract void widgetLayout();
		
		public <T> void propertyUpdated(T newvalue)
		{
			synchronized(pm)
			{
				pm.propertyUpdated(p, newvalue);
			}
			if (p.getShared() && pm.propertyManagerGroup != null)
				pm.propertyManagerGroup.broadcast(pm, p, newvalue);
			
			PropertyWidget saved = p.getPropertyWidget();
			p.setPropertyWidget(null);
			p.setValue(newvalue);
			p.setPropertyWidget(saved);
		}
		
		/**
		 * sets the actual widgets to reflect the value change
		 * @param newvalue
		 */
		protected abstract <T> void setPropertyValue(T newvalue);
		
		public <T> void setProperty(T newvalue)
		{
			changeFromOutside = true;
			setPropertyValue(newvalue);
			changeFromOutside = false;
		}
		
		protected void setPropertyVisible(boolean v)
		{
			if (this.getParent() != null)
				this.getParent().setVisible(v);
			if (this.pm != null)
			{
				this.pm.processLastDivider();
				this.pm.revalidate();
			}
			
		}
		protected abstract void setPropertyReadOnly(boolean v);
		
						
	}
	
	abstract class PropertyWidgetFactory {
		
		abstract PropertyWidget createWidget();
		
	}	
	

	protected void addProperty(Property p) throws Exception {
		
		
		if (acceptedTypes.indexOf(p.getValue().getClass()) < 0)
			throw new Exception();
		
		PropertyWidget pw = widgetFactories.get(acceptedTypes.indexOf(p.getValue().getClass())).createWidget();
		p.setPropertyWidget(pw);
		
		int preferredWidth = 180;	
	
		
		String[] split = p.getName().split("\\.");
		JPanel parent = this;
		String s = "";
		
		final JPanel thisPM = this;
		
		for (int i=0; i<split.length-1; i++)
		{
			s = s + split[i];
			preferredWidth -= 10;
			
			int index = groupNames.indexOf(s);
			if (index >= 0) 
				parent = groupPanels.get(index);
			else
			{
				final JPanel parent2 = new JPanel();
				groupStates.add(new Boolean(true));
				
				final String title = split[i]; 
								
				parent2.setBorder(BorderFactory.createTitledBorder(title + "    -"));
				parent2.setLayout(new BoxLayout(parent2, BoxLayout.Y_AXIS));	
				
				JPanel wrapper = new JPanel();
				wrapper.setBorder(null);
				wrapper.setLayout(new BoxLayout(wrapper,BoxLayout.X_AXIS));
				wrapper.add(Box.createRigidArea(new Dimension(preferredWidth,1)));
				parent2.add(wrapper);
				
				wrapper = new JPanel();
				wrapper.setBorder(null);
				wrapper.setLayout(new BoxLayout(wrapper, BoxLayout.Y_AXIS));
		
				wrapper.add(parent2);
			
				
				parent.add(wrapper, this.getComponentCount());
				
				
				parent2.addMouseListener(new MouseListener()
				{
					public void mouseClicked(MouseEvent e) {
						if (e.getY() < 20)
						{
							int index = groupPanels.indexOf(parent2);							
							Boolean visible = groupStates.get(index);
							
							if (visible.booleanValue())
							{
								groupStates.set(index,new Boolean(false));
								parent2.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED),title + "    +"));
								//parent2.setBorder(BorderFactory.createTitledBorder(title + "    +"));
								
								int maxx = 0;
								for (int i=0; i<parent2.getComponentCount(); i++)
								{
									int sx = parent2.getComponent(i).getWidth();
									if (sx > maxx) maxx = sx;
								}
								
								System.out.println(maxx);
								
							
								parent2.add(Box.createRigidArea(new Dimension(maxx,0)),0);
								
								for (int i=1; i<parent2.getComponentCount(); i++)
									parent2.getComponent(i).setVisible(false);
								
								for (int i=0; i<thisPM.getComponentCount(); i++)
									System.out.println(thisPM.getComponent(i));
								
								parent2.revalidate();
							}
							else
							{
								groupStates.set(index,new Boolean(true));
								
								parent2.remove(0);
								
								parent2.setBorder(BorderFactory.createTitledBorder(title + "    -"));
								
								parent2.setMinimumSize(new Dimension(10,10));
								
								for (int i=0; i<parent2.getComponentCount(); i++)
									parent2.getComponent(i).setVisible(true);
								
								parent2.revalidate();
							
							}
							
							thisPM.revalidate();
						}
					}

					@Override
					public void mouseEntered(MouseEvent arg0) {	}
					@Override
					public void mouseExited(MouseEvent arg0) {}
					@Override
					public void mousePressed(MouseEvent arg0) {}
					@Override
					public void mouseReleased(MouseEvent arg0) {}
					
				});
				
				groupNames.add(s);
				groupPanels.add(parent2);
				
				parent = parent2;
			}			
			s = s + ".";			
		}
		
		JPanel wrapper = new JPanel();
		wrapper.setBorder(null);
		wrapper.setLayout(new BoxLayout(wrapper, BoxLayout.Y_AXIS));	
		
		wrapper.add(pw);
		
		parent.add(wrapper, parent.getComponentCount());
		
		pw.setReferences(p, this);
		pw.widgetLayout();
		props.add(p);
		widgets.add(pw);		
		
		pw.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED, new Color(200,200,230), new Color(230,230,240)),BorderFactory.createEmptyBorder(2,2,2,2)));
		pw.setBackground(new Color(220,220,230));		
		
		processLastDivider();
		
		this.revalidate();
		
		
	}
	
	protected void removeProperty(String name)
	{
		groupPanels.add(0,this);
		for (int i=0; i<props.size(); i++)
			if (props.get(i).getName().equals(name))
			{
				Property p = props.get(i);
				PropertyWidget pw = widgets.get(i);
				p.setPropertyWidget(null);
				props.remove(i);
				widgets.remove(i);				
				
				for (int j=0; j<groupPanels.size(); j++)
				{
					if (pw.getParent().getParent() == groupPanels.get(j)) //double parent to go through the wrapper
					{
						groupPanels.get(j).remove(pw.getParent());						
						
						if (groupPanels.get(j).getComponentCount() == 0 && groupPanels.get(j) != this)
							groupPanels.get(j).getParent().getParent().remove(groupPanels.get(j).getParent());
					}
				}
			}
		
		groupPanels.remove(0);
		
	
		
		
		processLastDivider();
		
		this.revalidate();
	}
	
	private void processLastDivider()
	{

		
		groupPanels.add(0,this);
		
		for (int i=0; i<groupPanels.size(); i++)
		{
			//each wrapper has to contain a divider except the last VISIBLE one
			int lastVisible = groupPanels.get(i).getComponentCount()-1;
			while (!groupPanels.get(i).getComponent(lastVisible).isVisible())
				lastVisible--;
			
			for (int j=0; j<groupPanels.get(i).getComponentCount(); j++)
			{
				if (j==0)
					continue;
				
				JPanel w = (JPanel)groupPanels.get(i).getComponent(j);
				
				if (w.getComponentCount() == 1 && j != lastVisible)
				{
					w.add(Box.createRigidArea(new Dimension(1,3)));				
				}
				else if (w.getComponentCount() > 1 && j == lastVisible)
				{
					while (w.getComponentCount() > 1)
						w.remove(w.getComponentCount()-1);
				}
			}
		}		
		groupPanels.remove(0);
	}
	
	protected Property getProperty(String n) {
		for (int i=0; i<props.size(); i++)
			if (props.get(i).getName().equals(n))
				return props.get(i);
		return null;
	}
	
	public <T> void propertyUpdated(Property p, T newvalue)
	{
	}
	
	public <T> void receivePropertyBroadcast(Property p, T newvalue)
	{
		propertyUpdated(p, newvalue);
		
		int index = props.indexOf(p);
		if (index >=0 && index < widgets.size())
			widgets.get(index).setProperty(newvalue);

	}
	
	public void registerNewType(java.lang.Class c, PropertyWidgetFactory pwf) {		
		acceptedTypes.add(c);
		widgetFactories.add(pwf);		
	}	
	
	class StringPropertyWidget extends PropertyWidget {
		
		JTextField control = null;
		JLabel readOnlyControl = null;
		
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			this.add(new JLabel(this.p.getDisplayName()));

			final PropertyWidget th = this;
			
			control = new JTextField();
			control.setText((String)th.p.getValue());
			control.setMaximumSize(new Dimension(70,20));
			control.setPreferredSize(new Dimension(70,20));
			
			readOnlyControl = new JLabel();
			readOnlyControl.setText((String)th.p.getValue());
			//readOnlyControl.setMaximumSize(new Dimension(200,20));
			
			control.addActionListener(new java.awt.event.ActionListener() {
			    public void actionPerformed(java.awt.event.ActionEvent e) {

			        th.propertyUpdated(control.getText());      
			     }
			});
			
			this.add(Box.createRigidArea(new Dimension(5,1)));
			this.add(control);	
			this.add(Box.createHorizontalGlue());
		
			
			setPropertyReadOnly(p.getReadOnly());
			
			
		}		
		
		public <T> void setPropertyValue(T newvalue)
		{
			control.setText((String)newvalue);
		}


		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
			{
				if (r)
				{
					this.remove(control);					
					this.add(readOnlyControl,2);
				}
				else
				{
					this.remove(readOnlyControl);					
					this.add(control,2);
				}
			}
		}
	}
	
	class DoublePropertyWidget extends PropertyWidget {
		
		JTextField control = null;
		JLabel readOnlyControl = null;
		
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			this.add(new JLabel(this.p.getDisplayName()));

			final PropertyWidget th = this;
			
			control = new JTextField();
			control.setText("" + th.p.getValue());
			control.setMaximumSize(new Dimension(70,20));
			control.setPreferredSize(new Dimension(70,20));
			
			readOnlyControl = new JLabel();
			readOnlyControl.setText("" + th.p.getValue());
			//readOnlyControl.setMaximumSize(new Dimension(200,20));
			
			control.addActionListener(new java.awt.event.ActionListener() {
			    public void actionPerformed(java.awt.event.ActionEvent e) {
			        th.propertyUpdated(Double.parseDouble(control.getText()));      
			     }
			});
			
			//this.add(Box.createRigidArea(new Dimension(5,1)));
			this.add(Box.createHorizontalGlue());
			this.add(control);	
			
			
			
			setPropertyReadOnly(p.getReadOnly());
		}		
		
		public <T> void setPropertyValue(T newvalue)
		{
			control.setText(""+newvalue);
		}



		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
			{
				if (r)
				{
					this.remove(control);					
					this.add(readOnlyControl,2);
				}
				else
				{
					this.remove(readOnlyControl);					
					this.add(control,2);
				}
			}
			
		}
	}
	
	class IntegerPropertyWidget extends PropertyWidget {
		
		JSpinner control = null;
		JLabel readOnlyControl = null;
		public void widgetLayout()
		{			
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			
			//this.add(Box.createHorizontalGlue());
			this.add(new JLabel(this.p.getDisplayName()));
			
			final PropertyWidget th = this;
			
			control = new JSpinner();
			control.setValue((Integer)th.p.getValue());
			
			control.setPreferredSize(new Dimension(100,20));
			control.setMaximumSize(new Dimension(100,20));
			
			readOnlyControl = new JLabel();
			readOnlyControl.setText((Integer)th.p.getValue()+"");
			
			
			ChangeListener listener = new ChangeListener() {
			      public void stateChanged(ChangeEvent e) {
			    	 th.propertyUpdated(new Integer(((JSpinner)e.getSource()).getValue().toString()));			        
			      }
			    };
			control.addChangeListener(listener);

			//this.add(Box.createRigidArea(new Dimension(5,1)));
			this.add(Box.createHorizontalGlue());
			this.add(control);	
			
			
			
			setPropertyReadOnly(p.getReadOnly());
		}
		
		public <T> void setPropertyValue(T newvalue)
		{
			control.setValue((Integer)newvalue);
		}



		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
			{
				if (r)
				{
					this.remove(control);					
					this.add(readOnlyControl,2);
				}
				else
				{
					this.remove(readOnlyControl);					
					this.add(control,2);
				}
			}			
		}
	}
	
	public class Options
	{
		public int selectedIndex;
		public String[] options;
	}
	
	class OptionsPropertyWidget extends PropertyWidget {
		
		JComboBox control = null;
		
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			this.add(new JLabel(this.p.getDisplayName()));
			
			final PropertyWidget th = this;
			
			control = new JComboBox(((Options)th.p.getValue()).options);
			
			control.setPreferredSize(new Dimension(100,20));
			control.setMaximumSize(new Dimension(100,20));
			
			ActionListener listener = new ActionListener() {
			      public void actionPerformed(ActionEvent e) {
			    	  Options o = new Options();
			    	  o.options = ((Options)th.p.getValue()).options;
			    	  o.selectedIndex = control.getSelectedIndex();
			    	 th.propertyUpdated(o);			        
			      }
			    };			    
		
			control.addActionListener(listener);
			
			//this.add(Box.createRigidArea(new Dimension(5,1)));
			this.add(Box.createHorizontalGlue());
			this.add(control);
			
			setPropertyReadOnly(p.getReadOnly());
			setPropertyVisible(p.getVisible());
		}
		
		public <T> void setPropertyValue(T newvalue)
		{
			control.setSelectedIndex(((Options)newvalue).selectedIndex);
		}

		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
				control.setEnabled(!r);
			
			
		}
	}	
	
	
	public class List
	{
		public int[] selectedIndeces;
		public String[] items;
		
		public boolean prefixSearchable = true;
	}
	
	class ListPropertyWidget extends PropertyWidget {
		
		JList<String> control = null;
		
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			
			JPanel subpanel = new JPanel();
			subpanel.setBorder(null);
			//subpanel.setLayout(new BoxLayout(subpanel, BoxLayout.Y_AXIS));
			subpanel.setLayout(new BorderLayout());
			
			this.add(subpanel);
			
			subpanel.add(new JLabel(this.p.getDisplayName()),BorderLayout.NORTH);
			
			subpanel.setOpaque(false);
			
			final PropertyWidget th = this;
			
						
			control = new JList<String>(((List)th.p.getValue()).items);
			
			JScrollPane listScroller = new JScrollPane(control);
			listScroller.setPreferredSize(new Dimension(180, 80));			
			//listScroller.setMaximumSize(new Dimension(180,500));
			
			ListSelectionListener listener = new ListSelectionListener() {
				@Override
				public void valueChanged(ListSelectionEvent e) {
					if (e.getValueIsAdjusting() == false)
					{
						List l = new List();
				    	l.items = ((List)th.p.getValue()).items;
				    	l.selectedIndeces = control.getSelectedIndices();
				    	th.propertyUpdated(l);		   
					}					
				}
			    };
			    
			   KeyListener keyListener = new KeyListener()
			    {
				   String full = "";
				   long lastType = -1;

					@Override
					public void keyPressed(KeyEvent arg0) {
						// TODO Auto-generated method stub
						
					}

					@Override
					public void keyReleased(KeyEvent arg0) {
						// TODO Auto-generated method stub
						
					}

					@Override
					public void keyTyped(KeyEvent e) {
						Date d = new Date();
							if (d.getTime() - lastType > 500)
								full = "";	
							
							full = full + e.getKeyChar();
							
							for (int i=0; i < control.getModel().getSize(); i++) {
							    String str = ((String)control.getModel().getElementAt(i)).toLowerCase();
							    if (str.startsWith(full)) {
							        control.setSelectedIndex(i); 
							        control.ensureIndexIsVisible(i); 
							        break;
							    }
							}
							
							lastType = d.getTime();
							
					}
			    	
			    };
			   
					    
			 
			control.addListSelectionListener(listener);
			control.addKeyListener(keyListener);
		
			subpanel.add(listScroller,BorderLayout.CENTER);
			subpanel.invalidate();
			
			this.add(Box.createHorizontalGlue());

			
			
			
			setPropertyReadOnly(p.getReadOnly());
			setPropertyVisible(p.getVisible());
		}
		
		public <T> void setPropertyValue(T newvalue)
		{
			control.setListData(((List)newvalue).items);
			control.setSelectedIndices(((List)newvalue).selectedIndeces);			
		}

		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
				control.setEnabled(!r);	
		}
	}	
	
	
	
	class BooleanPropertyWidget extends PropertyWidget {
		
		JCheckBox control = null;
		
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			this.add(new JLabel(this.p.getDisplayName()));
			
			final PropertyWidget th = this;
			
			control = new JCheckBox();
			control.setSelected((Boolean)th.p.getValue());
			ChangeListener listener = new ChangeListener() {
			      public void stateChanged(ChangeEvent e) {
			    	  boolean b = control.isSelected();
			    	  System.out.println(b);
			    	  th.propertyUpdated(new Boolean(b));			        
			      }
			    };
			control.addChangeListener(listener);
			
			control.setBackground(new Color(0,0,0,0));

			//this.add(Box.createRigidArea(new Dimension(5,1)));
			this.add(Box.createHorizontalGlue());
			this.add(control);		
			//this.add(Box.createHorizontalGlue());
			
			
			setPropertyReadOnly(p.getReadOnly());
		}
		
		public <T> void setPropertyValue(T newvalue)
		{
			control.setSelected((Boolean)newvalue);
		}


		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
				control.setEnabled(!r);
		}
	}	
	
	public class OpenFile
	{
		public String path;
		
		public String[] extensions = new String[0];	
		public int currentExtension = -1;
		
		public boolean onlyFiles = true;
		public boolean onlyDirectories = false;
		public boolean filesAndDirectories = false;
		
		public String dialogTitle = "";
	}
	
	class OpenFilePropertyWidget extends PropertyWidget {
		
		FileFilter[] fileFilters;
		JButton control = null;
		
		public void widgetLayout()
		{
			
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			final PropertyWidget th = this;
			
			control = new JButton(this.p.getDisplayName(), new ImageIcon(Toolkit.getDefaultToolkit().getImage("Open16.gif")));
			//control.setMaximumSize(new Dimension(2000,20));
			control.setPreferredSize(new Dimension(130,20));
			ActionListener listener = new ActionListener() {
			      public void actionPerformed(ActionEvent e) {
			    	  
			    	  JFileChooser fc = new JFileChooser();
			    	  
			    	  final OpenFile prop = (OpenFile)th.p.getValue();
			    	  
			    	  if (prop.dialogTitle.length() > 0) fc.setDialogTitle(prop.dialogTitle);
			    	  
			    	  if (prop.extensions.length > 0)
			    	  {
			    		  fc.setAcceptAllFileFilterUsed(false);
			    		  
			    		  fileFilters = new FileFilter[prop.extensions.length];
			    		  
			    		  for (int i=0; i<prop.extensions.length; i++)
			    		  {
			    			  final int ii = i;
			    			  FileFilter ff = new FileFilter(){			    				  
			    				  @Override
			    				  public boolean accept(File f) {
			    					  if (f.isDirectory() || prop.extensions[ii].equals("*"))
			    						  return true;
			    					 

			    					String extension = getExtension(f);
								    if (extension != null && extension.equals(prop.extensions[ii]))
								    	return true;
								    else
								    	return false;
			    				  }
			    			  			

			    				  @Override
			    				  public String getDescription() {
			    					  if (prop.extensions[ii].equals("*"))
			    						  return "All Files";
			    					  return prop.extensions[ii]; 
									}			    			  
			    			  };
			    			  
			    			  fc.addChoosableFileFilter(ff);
			    			  
			    			  fileFilters[i] = ff;
			    		  }
			    		  
			    		  if (prop.currentExtension >=0 && prop.currentExtension<fileFilters.length)
			    			  fc.setFileFilter(fileFilters[prop.currentExtension]);
			    		
			    	  }
			    	  
			    	  if (prop.onlyDirectories)
			    		  fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			    	  else if (prop.filesAndDirectories)
			    		  fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
			    	  			    	  
			    	  int returnVal = fc.showOpenDialog(th);
			    	  if (returnVal == JFileChooser.APPROVE_OPTION) {
			    		  OpenFile v = new OpenFile();
			    		  v.path = fc.getSelectedFile().getAbsolutePath();
			              v.extensions = prop.extensions;
			              v.onlyDirectories = prop.onlyDirectories;
			              v.onlyFiles = prop.onlyFiles;
			              v.filesAndDirectories = prop.filesAndDirectories;
			              
			              th.propertyUpdated(v);	
			    	  }
			      }
			    };
			control.addActionListener(listener);

			this.add(Box.createHorizontalGlue());
			this.add(control);
			this.add(Box.createHorizontalGlue());
			
			setPropertyReadOnly(p.getReadOnly());
			setPropertyVisible(p.getVisible());
		}
		
		public <T> void setPropertyValue(T newvalue)
		{
			this.propertyUpdated(newvalue);
		}
		
		 private String getExtension(File f) {
		        String ext = null;
		        String s = f.getName();
		        int i = s.lastIndexOf('.');
		 
		        if (i > 0 &&  i < s.length() - 1) {
		            ext = s.substring(i+1).toLowerCase();
		        }
		        return ext;
		    }

		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
				control.setEnabled(!r);
			
		}
	}	

	public class SaveFile
	{
		public String path;
		public String[] extensions;	
		
		public int currentExtension = -1;
		
		public boolean onlyFiles = true;
		public boolean onlyDirectories = false;
		public boolean filesAndDirectories = false;
		
		public String dialogTitle = "";
	}
	
	class SaveFilePropertyWidget extends PropertyWidget {
		
		JButton control = null;
		FileFilter[] fileFilters;
		
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			final PropertyWidget th = this;
			
			control = new JButton(this.p.getDisplayName(), new ImageIcon(Toolkit.getDefaultToolkit().getImage("Save16.gif")));			
			control.setPreferredSize(new Dimension(130,20));
			ActionListener listener = new ActionListener() {
			      public void actionPerformed(ActionEvent e) {
			    	  
			    	  JFileChooser fc = new JFileChooser();
			    	  
			    	  final SaveFile prop = (SaveFile)th.p.getValue();
			    	  
			    	  if (prop.dialogTitle.length() > 0) fc.setDialogTitle(prop.dialogTitle);
			    	  
			    	  if (prop.extensions.length > 0)
			    	  {
			    		  fc.setAcceptAllFileFilterUsed(false);
			    		  
			    		  fileFilters = new FileFilter[prop.extensions.length];
			    		  
			    		  for (int i=0; i<prop.extensions.length; i++)
			    		  {
			    			  final int ii = i;
			    			  FileFilter ff = new FileFilter(){			    				  
			    				  @Override
			    				  public boolean accept(File f) {
			    					  if (f.isDirectory() || prop.extensions[ii].equals("*"))
			    						  return true;
			    					 

			    					String extension = getExtension(f);
								    if (extension != null && extension.equals(prop.extensions[ii]))
								    	return true;
								    else
								    	return false;
			    				  }
			    			  			

			    				  @Override
			    				  public String getDescription() {
			    					  if (prop.extensions[ii].equals("*"))
			    						  return "All Files";
			    					  return prop.extensions[ii]; 
									}			    			  
			    			  };
			    			  
			    			  fc.addChoosableFileFilter(ff);
			    			  
			    			  fileFilters[i] = ff;
			    		  }
			    		  
			    		  if (prop.currentExtension >=0 && prop.currentExtension<fileFilters.length)
			    			  fc.setFileFilter(fileFilters[prop.currentExtension]);
			    		
			    	  }
			    	  
			    	  if (prop.onlyDirectories)
			    		  fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			    	  else if (prop.filesAndDirectories)
			    		  fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
			    	  			    	  
			    	  int returnVal = fc.showOpenDialog(th);
			    	  if (returnVal == JFileChooser.APPROVE_OPTION) {
			    		  OpenFile v = new OpenFile();
			    		  v.path = fc.getSelectedFile().getAbsolutePath();
			              v.extensions = prop.extensions;
			              v.onlyDirectories = prop.onlyDirectories;
			              v.onlyFiles = prop.onlyFiles;
			              v.filesAndDirectories = prop.filesAndDirectories;
			              
			              th.propertyUpdated(v);	
			    	  }
			      }
			    };
			control.addActionListener(listener);

			this.add(Box.createHorizontalGlue());
			this.add(control);
			this.add(Box.createHorizontalGlue());
			
			setPropertyReadOnly(p.getReadOnly());
			setPropertyVisible(p.getVisible());
		}
		
		 private String getExtension(File f) {
		        String ext = null;
		        String s = f.getName();
		        int i = s.lastIndexOf('.');
		 
		        if (i > 0 &&  i < s.length() - 1) {
		            ext = s.substring(i+1).toLowerCase();
		        }
		        return ext;
		    }
		
		public <T> void setPropertyValue(T newvalue)
		{
			this.propertyUpdated(newvalue);
		}

		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
				control.setEnabled(!r);
			
		}
	}	

	
	public class Percent
	{
		private double val = 0.5;
		
		public Percent(double v)
		{
			setRatio(v);
		}
		public Percent(int p)
		{
			setPercent(p);
		}
		public void setPercent(int vv)
		{
			double v = vv/100.;
			setRatio(v);
		}
		public void setRatio(double v)
		{
			if (v < 0) val = 0;
			else if (v > 1) val = 1;
			else val = v;		
		}		
		public int getPercent()
		{
			return (int)(val*100);
		}
		public double getRatio()
		{
			return val;
		}
	}
	
	class PercentPropertyWidget extends PropertyWidget {
		
		JSlider control = null;
		JLabel readOnlyControl = null;
		public void widgetLayout()
		{			
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			this.add(new JLabel(this.p.getDisplayName()));
			
			final PropertyWidget th = this;
			
			control = new JSlider(0,100);
			control.setValue(((Percent)th.p.getValue()).getPercent());
			control.setPreferredSize(new Dimension(70,20));
			control.setBackground(new Color(0,0,0,0));
			
			readOnlyControl = new JLabel();
			readOnlyControl.setText(((Percent)th.p.getValue()).getPercent()+"%");
			
			
			ChangeListener listener = new ChangeListener() {
			      public void stateChanged(ChangeEvent e) {
			    	 th.propertyUpdated(new Percent(((JSlider)e.getSource()).getValue()));			        
			      }
			    };
			control.addChangeListener(listener);

			//this.add(Box.createRigidArea(new Dimension(5,1)));
			this.add(Box.createHorizontalGlue());
			this.add(control);			
		
			
			setPropertyReadOnly(p.getReadOnly());
		}
		
		public <T> void setPropertyValue(T newvalue)
		{
			control.setValue(((Percent)newvalue).getPercent());
		}



		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
			{
				if (r)
				{
					this.remove(control);					
					this.add(readOnlyControl,2);
				}
				else
				{
					this.remove(readOnlyControl);					
					this.add(control,2);
				}
			}			
		}
	}
	
	
	class ColorPropertyWidget extends PropertyWidget {
		
		JButton control = null;
				
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			final PropertyWidget th = this;
			
			control = new JButton(this.p.getDisplayName(), new ImageIcon(Toolkit.getDefaultToolkit().getImage("color_picker.GIF")));
			//control = new JButton("coo");
			control.setMaximumSize(new Dimension(2000,20));
			control.setPreferredSize(new Dimension(130,20));
			ActionListener listener = new ActionListener() {
			      public void actionPerformed(ActionEvent e) {  
			    	 
			    	  Color newColor = JColorChooser.showDialog(th,"Choose Color",(Color)th.p.getValue());
			    	  th.propertyUpdated(newColor);
			    	  
			      }
			    };
			control.addActionListener(listener);

			this.add(Box.createHorizontalGlue());
			this.add(control);
			this.add(Box.createHorizontalGlue());
			
			setPropertyReadOnly(p.getReadOnly());
			setPropertyVisible(p.getVisible());
		}		
		
		public <T> void setPropertyValue(T newvalue)
		{
			this.propertyUpdated(newvalue);
		}

		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
				control.setEnabled(!r);	
		}
	}	
        
        
        
        
        class ButtonPropertyWidget extends PropertyWidget {
		
		JButton control = null;
				
		public void widgetLayout()
		{
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			final PropertyWidget th = this;
			
			control = new JButton(this.p.getDisplayName());
			//control = new JButton("coo");
			control.setMaximumSize(new Dimension(2000,20));
			control.setPreferredSize(new Dimension(130,20));
			ActionListener listener = new ActionListener() {
			      public void actionPerformed(ActionEvent e) {  
			    	 
			    	//
			    	  th.propertyUpdated(control.getText());
			      }
			    };
			control.addActionListener(listener);

			this.add(Box.createHorizontalGlue());
			this.add(control);
			this.add(Box.createHorizontalGlue());
			
			setPropertyReadOnly(p.getReadOnly());
			setPropertyVisible(p.getVisible());
		}		
		
		public <T> void setPropertyValue(T newvalue)
		{                
                    
			this.propertyUpdated(newvalue);
		}

		@Override
		protected void setPropertyReadOnly(boolean r) {
			if (control != null)
				control.setEnabled(!r);	
		}
	}	
        
        
	
	
	
	public PropertyManager(String name) {
		
		
		this.name = name;
		
		acceptedTypes = new ArrayList<java.lang.Class>();
		
		widgetFactories = new ArrayList<PropertyWidgetFactory>();
		
		props = new ArrayList<Property>();
		widgets = new ArrayList<PropertyWidget>();
		
				
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
		
		
		
		
		
		
		JPanel wrapper = new JPanel();
		wrapper.setBorder(null);
		wrapper.setLayout(new BoxLayout(wrapper,BoxLayout.X_AXIS));
		wrapper.add(Box.createRigidArea(new Dimension(180,1)));		
		
		
		
		
		this.add(wrapper);		
	
		
		setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createTitledBorder("Properties"),
                BorderFactory.createEmptyBorder(5,5,5,5)));
		
		
		
		//add some standard types
		Class c = new String().getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new StringPropertyWidget();
			}
		});
		
		c = new Double(0).getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new DoublePropertyWidget();
			}
		});		
		
		c = new Integer(0).getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new IntegerPropertyWidget();
			}
		});
		
		c = new Boolean(true).getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new BooleanPropertyWidget();
			}
		});	
		
		c = new Options().getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new OptionsPropertyWidget();
			}
		});			
		
		c = new OpenFile().getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new OpenFilePropertyWidget();
			}
		});			

		
		c = new SaveFile().getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new SaveFilePropertyWidget();
			}
		});	
		
		c = new Percent(0).getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new PercentPropertyWidget();
			}
		});	
		
		c = new Color(1,1,1).getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new ColorPropertyWidget();
			}
		});	
		
		c = new List().getClass();		
		registerNewType(c, new PropertyWidgetFactory() {
			PropertyWidget createWidget() {
					return new ListPropertyWidget();
			}
		});	
		
		c= new Button().getClass();
                registerNewType(c, new PropertyWidgetFactory(){
                   PropertyWidget createWidget(){
                       return new ButtonPropertyWidget(); 
                   } 
                });
	}
	
	public void setPropertyManagerGroup(PropertyManagerGroup g)
	{
		propertyManagerGroup = g;		
	}
	
	public TaskObserverDialog getTaskObserverDialog()
	{
		return taskObserverDialog;
	}
	
        
        
        public class Button{
            
        }

	
}
